package com.atguigu.tingshu.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.delay.DelayMessageService;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private AlbumFeignClient albumFeignClient;


    /**
     * 用户下单返回渲染订单确认页面数据
     * 为用户选择购买不同商品返回订单确认数据
     * 1.VIP会员  2.专辑  3.声音
     *
     * @param tradeVo itemType->项目类型: 1001-专辑 1002-声音 1003-vip会员
     * @return
     */
    @Override
    public OrderInfoVo getOrderTradeData(TradeVo tradeVo) {
        Long userId = AuthContextHolder.getUserId();
        //1.创建响应VO对象，将VO中需要相关价格，商品明细，优惠明细定义
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //1.1 原价：初始0.00
        BigDecimal originalAmount = new BigDecimal("0.00");
        //1.2 减免：初始0.00
        BigDecimal derateAmount = new BigDecimal("0.00");
        //1.3 订单：初始0.00
        BigDecimal orderAmount = new BigDecimal("0.00");
        //1.4 订单明细（商品）列表
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //1.5 订单优惠列表
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();

        //2.处理购买项目类型为VIP会员 itemType=1003-vip会员  允许多次仅购买
        if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(tradeVo.getItemType())) {
            //2.1 远程调用"用户服务"获取套餐信息
            VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData();
            Assert.notNull(vipServiceConfig, "VIP套餐：{}不存在！", tradeVo.getItemId());
            //2.2 封装订单价格 （原价，减免价，订单价）
            originalAmount = vipServiceConfig.getPrice();
            orderAmount = vipServiceConfig.getDiscountPrice();
            derateAmount = originalAmount.subtract(orderAmount);
            //2.3 封装订单中商品明细列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName("VIP会员：" + vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            //订单明细价格为原价
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //2.4 封装优惠明细列表
            if (originalAmount.compareTo(orderAmount) != 0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("VIP限时优惠价：" + orderAmount);
                orderDerateVoList.add(orderDerateVo);
            }
        } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(tradeVo.getItemType())) {
            //3.处理购买项目类型为专辑 itemType=1001-专辑
            Long albumId = tradeVo.getItemId();
            //3.1 远程调用“用户”服务判断是否已购专辑 已购买则抛出异常 true
            Boolean isPaid = userFeignClient.isPaidAlbum(albumId).getData();
            Assert.state(!isPaid, "请勿重复购买！");
            //3.2 远程调用“`用户”服务判获取用户信息断是否VIP
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Assert.notNull(userInfoVo, "用户信息有误！");
            Boolean isVIP = false;
            if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                isVIP = true;
            }
            //3.3 远程调用“专辑”服务获取专辑信息（价格，不同用户折扣）
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfo, "专辑信息有误！");
            //3.4 根据专辑不同折扣计算不同用户身份价格
            //原价
            originalAmount = albumInfo.getPrice();
            //暂时认为无折扣，订单价=原价
            orderAmount = originalAmount;
            //3.4.1 判断当前专辑是否有普通用户折扣  订单价=原价*折扣     100 * 8 / 10
            if (albumInfo.getDiscount().intValue() != -1 && !isVIP) {
                // 折扣存在*.**避免精度问题 设置保留小数位，设置四舍五入
                orderAmount = originalAmount.multiply(albumInfo.getDiscount())
                        .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }
            //3.4.2 判断当前专辑是否有VIP用户折扣
            if (albumInfo.getVipDiscount().intValue() != -1 && isVIP) {
                orderAmount = originalAmount.multiply(albumInfo.getVipDiscount())
                        .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }
            //3.5 封装商品明细列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(albumId);
            orderDetailVo.setItemName("专辑：" + albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //3.6 封装商品优惠列表
            if (originalAmount.compareTo(orderAmount) != 0) {
                derateAmount = originalAmount.subtract(orderAmount);
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("专辑折扣，减免：" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }
        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(tradeVo.getItemType())) {
            //4.处理购买项目类型为声音 1002-声音
            //4.1 远程调用专辑服务得到待结算声音列表
            Long trackId = tradeVo.getItemId();
            List<TrackInfo> waitBuyTrackInfoList = albumFeignClient.getWaitBuyTrackInfoList(trackId, tradeVo.getTrackCount()).getData();
            Assert.notNull(waitBuyTrackInfoList, "无符合要求结算商品");
            //4.2 远程调用“专辑服务”获取专辑信息得到声音价格
            Long albumId = waitBuyTrackInfoList.get(0).getAlbumId();
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            BigDecimal price = albumInfo.getPrice();

            //4.3 基于待结算声音列表 计算订单价格
            originalAmount = price.multiply(BigDecimal.valueOf(waitBuyTrackInfoList.size()));
            orderAmount = originalAmount;
            //4.4 基于待结算声音列表 封装商品明细列表
            orderDetailVoList = waitBuyTrackInfoList
                    .stream()
                    .map(trackInfo -> {
                        OrderDetailVo orderDetailVo = new OrderDetailVo();
                        orderDetailVo.setItemId(trackInfo.getId());
                        orderDetailVo.setItemName(trackInfo.getTrackTitle());
                        orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                        orderDetailVo.setItemPrice(price);
                        return orderDetailVo;
                    }).collect(Collectors.toList());
        }
        //5.封装订单确认页所需价格，商品，优惠，以及"其他信息"
        //5.1 封装订单确认页中基本信息
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        //封装其他信息
        //TODO 付款方式暂不确定，不设置
        //5.2 购买项目类型
        orderInfoVo.setItemType(tradeVo.getItemType());
        //5.3 本次订单流水号-后续提交订单避免订单重复提交
        //5.3.1 生成流水号
        String tradeNo = IdUtil.randomUUID();
        //5.3.2 生成本次用户当前订单流水号key
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //5.3.3 存入Redis
        redisTemplate.opsForValue().set(tradeKey, tradeNo, 5, TimeUnit.MINUTES);
        //5.3.4 封装本次订单流水号
        orderInfoVo.setTradeNo(tradeNo);
        //5.4 生成本次订单时间戳以及签名
        orderInfoVo.setTimestamp(DateUtil.current());
        //5.4.1 将目前订单VO对象转为Map对象 将付款方式payWay参数值为空排除掉
        Map<String, Object> orderInfoVoMapParams = BeanUtil.beanToMap(orderInfoVo, false, true);
        //5.4.2 调用工具类对以后参数进行签名得到签名值
        String sign = SignHelper.getSign(orderInfoVoMapParams);
        orderInfoVo.setSign(sign);
        return orderInfoVo;
    }

    @Autowired
    private AccountFeignClient accountFeignClient;

    @Autowired
    private DelayMessageService delayMessageService;

    /**
     * 当前用户提交订单
     *
     * @param userId      用户ID
     * @param orderInfoVo 订单VO信息
     * @return {"orderNo":"订单唯一编号"}
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Map<String, String> submitOrder(Long userId, OrderInfoVo orderInfoVo) {
        //1.业务校验-验证流水号-避免回退导致重复提交
        //1.1 从Redis中获取当前用户订单流水号
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //1.2 判断用户提交流水号跟存在Redis中是否一致
        String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(scriptText, Boolean.class);
        Boolean flag = (Boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), orderInfoVo.getTradeNo());
        if (!flag) {
            throw new GuiguException(500, "流水号参数有误！");
        }

        //2.业务校验-验证签名-避免订单中数据被篡改
        //2.1 将提交OrderInfoVO对象转为Map 当初在生成签名"payWay"没有参与签名 故也要将支付方式排除掉
        Map<String, Object> mapParams = BeanUtil.beanToMap(orderInfoVo);
        mapParams.remove("payWay");
        //2.2 对再次提交订单参数进行一次加密得到新签名 跟 提交参数中旧签名比较
        SignHelper.checkSign(mapParams);

        //3.保存订单及订单明细以及优惠明细（未支付）
        OrderInfo orderInfo = this.saveOrderInfo(userId, orderInfoVo);

        //4.判断用户选择支付方式（VIP/专辑选择余额。声音仅支持余额支付）
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())) {
            //5.远程调用"账户"服务扣减余额
            //5.1 构建扣减余额VO对象
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderInfo.getOrderNo());
            accountDeductVo.setUserId(orderInfo.getUserId());
            accountDeductVo.setAmount(orderInfo.getOrderAmount());
            accountDeductVo.setContent(orderInfo.getOrderTitle());
            //5.2 远程调用完成扣减余额
            Result result = accountFeignClient.checkAndDeduct(accountDeductVo);
            //5.3 所有模块都采用全局异常处理方法，即使目标接口发生异常 全局异常处理类 仍然会返回Result，系统无法感知异常
            if (!ResultCodeEnum.SUCCESS.getCode().equals(result.getCode())) {
                throw new GuiguException(result.getCode(), result.getMessage());
            }
            //6.修改订单支付状态：已支付
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.updateById(orderInfo);

            //7.远程调用"用户"服务完成虚拟物品发货
            //7.1 构建虚拟物品发货VO对象
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
            userPaidRecordVo.setUserId(orderInfo.getUserId());
            userPaidRecordVo.setItemType(orderInfoVo.getItemType());
            List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
            //7.2 远程调用完成虚拟物品发货
            result = userFeignClient.savePaidRecord(userPaidRecordVo);
            if (!ResultCodeEnum.SUCCESS.getCode().equals(result.getCode())) {
                throw new GuiguException(result.getCode(), result.getMessage());
            }
        } else {
            //8. 选择微信支付，发送延迟任务，延迟关闭超时未支付订单 测试：30s  正式：1小时
            delayMessageService.sendDelayMsg(KafkaConstant.QUEUE_ORDER_CANCEL, orderInfo.getId().toString(), 3600);
        }
        //9.返回订单编号
        Map<String, String> map = new HashMap<>();
        map.put("orderNo", orderInfo.getOrderNo());
        return map;
    }


    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    /**
     * 保存订单，订单明细，优惠明细
     *
     * @param userId      用户ID
     * @param orderInfoVo 订单VO信息
     * @return
     */
    @Override
    public OrderInfo saveOrderInfo(Long userId, OrderInfoVo orderInfoVo) {
        //1.保存订单
        //1.1 将订单VO转为订单PO对象
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);
        //1.2 封装简单属性 ：用户ID 订单状态（未支付） 订单内容
        orderInfo.setUserId(userId);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
            String itemName = orderDetailVoList.get(0).getItemName();
            orderInfo.setOrderTitle(itemName);
        }
        //1.3 产生订单唯一编号
        String today = DateUtil.today().replaceAll("-", "");
        String orderNo = today + IdUtil.getSnowflakeNextId();
        orderInfo.setOrderNo(orderNo);
        //1.4 保存订单得到订单ID
        orderInfoMapper.insert(orderInfo);
        Long orderId = orderInfo.getId();
        //2.保存订单明细
        if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
            orderDetailVoList.
                    stream()
                    .forEach(orderDetailVo -> {
                        OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
                        orderDetail.setOrderId(orderId);
                        orderDetailMapper.insert(orderDetail);
                    });

        }
        //3.保存优惠明细
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (CollectionUtil.isNotEmpty(orderDerateVoList)) {
            orderDerateVoList.
                    stream()
                    .forEach(orderDerateVo -> {
                        OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);
                        orderDerate.setOrderId(orderId);
                        orderDerateMapper.insert(orderDerate);
                    });
        }
        return orderInfo;
    }

    /**
     * 根据订单编号查询订单信息
     *
     * @param orderNo
     * @return
     */
    @Override
    public OrderInfo getOrderInfo(String orderNo) {
        //1.根据订单编号查询订单信息
        LambdaUpdateWrapper<OrderInfo> orderInfoLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        orderInfoLambdaUpdateWrapper.eq(OrderInfo::getOrderNo, orderNo);
        OrderInfo orderInfo = orderInfoMapper.selectOne(orderInfoLambdaUpdateWrapper);
        if (orderInfo != null) {

            //2.根据订单ID查询订单明细列表
            LambdaUpdateWrapper<OrderDetail> orderDetailLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            orderDetailLambdaUpdateWrapper.eq(OrderDetail::getOrderId, orderInfo.getId());
            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(orderDetailLambdaUpdateWrapper);
            if (CollectionUtil.isNotEmpty(orderDetailList)) {
                orderInfo.setOrderDetailList(orderDetailList);
            }
            //3.根据订单ID查询订单优惠列表
            LambdaUpdateWrapper<OrderDerate> orderDerateLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            orderDerateLambdaUpdateWrapper.eq(OrderDerate::getOrderId, orderInfo.getId());
            List<OrderDerate> orderDerateList = orderDerateMapper.selectList(orderDerateLambdaUpdateWrapper);
            if (CollectionUtil.isNotEmpty(orderDerateList)) {
                orderInfo.setOrderDerateList(orderDerateList);
            }
        }
        return orderInfo;
    }

    /**
     * 查询当前用户订单分页列表
     *
     * @param pageInfo 分页对象
     * @param userId   用户ID
     * @return
     */
    @Override
    public Page<OrderInfo> getUserOrderByPage(Page<OrderInfo> pageInfo, Long userId) {
        return orderInfoMapper.getUserOrderByPage(pageInfo, userId);
    }

    /**
     * 检查订单状态及关闭订单
     *
     * @param orderId
     */
    @Override
    public void cancelOrder(Long orderId) {
        //1.根据订单ID查询订单状态
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo != null && SystemConstant.ORDER_STATUS_UNPAID.equals(orderInfo.getOrderStatus())) {
            //2.判断订单状态 如果是未支付，修改为已取消
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            orderInfoMapper.updateById(orderInfo);
            log.info("[订单服务]订单：{}超时未支付，取消订单", orderId);
        }
    }

    /**
     * 用户在线付款成功更新订单状态及虚拟物品发货
     *
     * @param orderNo
     * @return
     */
    @Override
    public void orderPaySuccess(String orderNo) {
        //1.根据订单编号查询订单信息
        OrderInfo orderInfo = this.getOrderInfo(orderNo);
        //2.判断订单状态
        if (SystemConstant.ORDER_STATUS_PAID.equals(orderInfo.getOrderStatus())) {
            return;
        }
        //3.更新订单状态：已支付
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
        orderInfoMapper.updateById(orderInfo);
        //4.远程调用用户服务完成虚拟物品发货
        //4.1 构建购买记录VO对象
        UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
        userPaidRecordVo.setOrderNo(orderNo);
        userPaidRecordVo.setUserId(orderInfo.getUserId());
        userPaidRecordVo.setItemType(orderInfo.getItemType());
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (CollectionUtil.isNotEmpty(orderDetailList)) {
            //获取购买项目ID（会员ID或专辑ID）
            List<Long> itemIdList = orderDetailList.stream().map(OrderDetail::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
        }
        //4.2 远程调用判断接口调用业务状态码
        Result result = userFeignClient.savePaidRecord(userPaidRecordVo);
        if (200 != result.getCode()) {
            throw new GuiguException(500, "购买记录保存失败");
        }
    }


}
